import base64
import calendar
import json
import logging
import re
import time
import uuid
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timezone

import requests
from Crypto.Cipher import DES
from Crypto.Util.Padding import pad

from product_upload.domain.basic.basic_product import BasicProduct
from product_upload.domain.basic.basic_sy import BasicSy
from product_upload.util.basic.common_util import sleep_random_duration
from product_upload.util.basic.mysql_util import db_batch_insert, db_get_one, db_list_by_page, db_batch_update
from product_upload.util.basic.openai_util import model_request

logger = logging.getLogger(__name__)
API_BASE_URL = "https://api.saleyee.cn"
APP_TOKEN = "E1bqIkDMKJIoXNzBL9+6oA=="
APP_KEY = "zb4NymQL"

"""
区域编码: SZ0001, 区域名称: US
区域编码: SZ0003, 区域名称: UK
区域编码: SZ0002, 区域名称: CN
区域编码: SZ0004, 区域名称: DE
区域编码: SZ0005, 区域名称: FR
区域编码: SZ0006, 区域名称: HK
区域编码: SZ0007, 区域名称: CZ
"""


def ai_gen_size(product_info):
    sys_prompt = """
    I will provide you with product information, please help me extract the length, width, and height (item_) of the product information. If you are not confident enough about the length, width, height, and weight of the product based on the product information, please fill in 1.
Note: All dimensions are in inches, and the total quantity is in pounds.
Strictly return in JSON format without explanation or any other irrelevant information. For example:
{"item_length":1,"item_width":1,"item_height":1,"item_weight":1}
    """
    user_prompt = f"product information:\n{product_info}"
    size_dict = model_request(True, user_prompt, sys_prompt, model="gpt-4o-mini")
    keys_are_identical = set(size_dict.keys()) == {"item_length", "item_width", "item_height"}
    if not keys_are_identical:
        sleep_random_duration()
        size_dict = model_request(True, user_prompt, sys_prompt)
    for k, v in size_dict.items():
        if v is None or not is_valid_positive_number(v):
            size_dict[k] = 1
    return size_dict


def is_valid_positive_number(s):
    try:
        v_float = float(s)
        return v_float > 0
    except ValueError:
        return False


# 拉取所有货仓
def get_warehouse() -> list:
    url = f"{API_BASE_URL}/api/Product/GetWarehouse"
    message_data = None
    # 发送请求
    response = make_saleyee_request(
        url=url,
        message_data=message_data,
        token=APP_TOKEN,
        des_key=APP_KEY
    )
    if not response or "error" in response:
        error_msg = response.get("error") if response else "未知错误"
        raise Exception(f"获取区域信息失败: {error_msg}")
    if response.get("Result") != 1:
        error = response.get("Error", {})
        error_msg = error.get("LongMessage") if error else "未知错误"
        raise Exception(f"获取区域信息失败: {error_msg}")
    try:
        if not response.get("Message"):
            return []
        warehouse_list = json.loads(response["Message"])
        return warehouse_list
    except json.JSONDecodeError:
        raise Exception("解析区域信息失败: 无效的JSON格式")


def filter_sy_html_data(html_content):
    if html_content is None or html_content == "":
        return ""
    cleaned_text = re.sub(r'<[^>]+>', '\n', html_content)
    non_ascii_pattern = re.compile(r'[^\x00-\x7F]+')
    cleaned_text_without_unicode = non_ascii_pattern.sub('', cleaned_text)
    entity_pattern = re.compile(r'&#[0-9]+;')
    final_cleaned_text = entity_pattern.sub('', cleaned_text_without_unicode)
    return final_cleaned_text.strip().replace("\n\n\n", "\n").replace("\n\n", "\n")


def encrypt_des(data: str, des_key: str) -> str:
    # DES使用CBC模式，key和iv相同
    des_iv = des_key  # IV同key
    cipher = DES.new(
        des_key.encode('utf-8'),
        DES.MODE_CBC,
        des_iv.encode('utf-8')
    )
    # PKCS7填充并加密
    padded_data = pad(data.encode('utf-8'), DES.block_size)
    encrypted_data = cipher.encrypt(padded_data)
    # 转换为base64编码
    return base64.b64encode(encrypted_data).decode('utf-8')


# 构建请求并加密Sign字段
# def make_saleyee_request(url: str, message_data: dict, token: str, des_key: str) -> dict:
#     # 构建请求体
#     request_body = {
#         "Version": "1.0.0.0",
#         "Token": token,
#         "Sign": None,
#         "RequestId": str(uuid.uuid4()),
#         "RequestTime": datetime.now(timezone.utc).strftime("%Y/%m/%d %H:%M:%S"),
#         "Message": json.dumps(message_data)
#     }
#     # 转换请求体为JSON字符串
#     json_str = json.dumps(request_body)
#     # 对请求体的JSON字符串加密
#     request_body["Sign"] = encrypt_des(json_str, des_key)
#     # 发送POST请求
#     try:
#         response = requests.post(url, json=request_body, timeout=30)
#         return response.json()
#     except requests.exceptions.Timeout:
#         return {"error": "请求超时"}
#     except requests.exceptions.RequestException as e:
#         return {"error": str(e)}


def make_saleyee_request(url: str, message_data: dict, token: str, des_key: str) -> dict:
    attempt = 0
    max_retries = 2
    while attempt < max_retries:
        # 构建请求体
        request_body = {
            "Version": "1.0.0.0",
            "Token": token,
            "Sign": None,
            "RequestId": str(uuid.uuid4()),
            "RequestTime": datetime.now(timezone.utc).strftime("%Y/%m/%d %H:%M:%S"),
            "Message": json.dumps(message_data)
        }
        # 转换请求体为JSON字符串
        json_str = json.dumps(request_body)
        # 对请求体的JSON字符串加密
        request_body["Sign"] = encrypt_des(json_str, des_key)
        try:
            # 发送POST请求
            response = requests.post(url, json=request_body, timeout=20)
            return response.json()
        except (requests.exceptions.Timeout, requests.exceptions.ConnectionError):
            if attempt == max_retries - 1:  # 如果这是最后一次尝试
                return {"error": "多次请求超时或连接错误"}
            else:
                attempt += 1  # 准备下一次尝试
        except requests.exceptions.RequestException as e:
            # 其他类型的请求异常直接返回错误信息
            return {"error": str(e)}


def query_product_inventory(skus: list[str]) -> dict:
    message_data = {"SkuList": skus}
    url = f"{API_BASE_URL}/api/Product/QueryProductInventory"
    response = make_saleyee_request(url=url, message_data=message_data, token=APP_TOKEN, des_key=APP_KEY)
    if not response or "error" in response:
        error_msg = response.get("error") if response else "未知错误"
        logger.error(f"SY fail query_product_inventory: {error_msg}")
        return {}

    if response.get("Result") != 1:
        error = response.get("Error", {})
        error_msg = error.get("LongMessage") if error else "未知错误"
        logger.error(f"SY fail query_product_inventory: {error_msg}")
        return {}
    try:
        if not response.get("Message"):
            return {}
        inventory_data = json.loads(response["Message"])
        price_res_dict = {}
        for inventory_item in inventory_data:
            sku = inventory_item.get("Sku", "")
            quantity = 0
            for quantity_lop in inventory_item.get("ProductInventorySiteList", []):
                for qty_item in quantity_lop.get("ProductInventoryList", []):
                    if qty_item.get("StockCode") == "SZ0001":
                        quantity = int(qty_item.get("Qty")) if qty_item.get("Qty") else 0
            price_res_dict[sku] = quantity
        return price_res_dict
    except json.JSONDecodeError:
        logger.error("SY fail query_product_inventory: error json")
        return {}


def query_product_price(skus: list[str]) -> dict:
    message_data = {"SkuList": skus}
    url = f"{API_BASE_URL}/api/Product/QueryProductPrice"
    response = make_saleyee_request(url=url, message_data=message_data, token=APP_TOKEN, des_key=APP_KEY)
    if not response or "error" in response:
        error_msg = response.get("error") if response else "not error"
        logger.error(f"SY fail query_product_price: {error_msg}")
        return {}
    if response.get("Result") != 1:
        error = response.get("Error", {})
        error_msg = error.get("LongMessage") if error else "not error"
        logger.error(f"SY fail query_product_price: {error_msg}")
        return {}
    try:
        if not response.get("Message"):
            return {}
        price_data = json.loads(response["Message"])
        # 进行拼接价格，如果没有美国仓的 视为下架
        price_res_dict = {}
        for price_lop in price_data:
            sku = price_lop.get("Sku", "")
            warehouse_list = price_lop.get("WarehousePriceList", [])
            sz0001_prices = []
            for warehouse in warehouse_list:
                if warehouse.get("StockCode", "") == "SZ0001" and warehouse.get("LogisticsProductName","") != "Self-Pick up":
                    # 获取 SellingPrice，默认为 0
                    price = float(warehouse.get("SellingPrice", 0))
                    # 如果 SellingPrice 为 0，则使用 OriginalPrice
                    if price == 0:
                        price = float(warehouse.get("OriginalPrice", 0))
                    sz0001_prices.append(price)
            if sz0001_prices:
                min_price = min(sz0001_prices)
                price_res_dict[sku] = round(min_price, 2)
            else:
                price_res_dict[sku] = 0.0
        return price_res_dict
    except json.JSONDecodeError:
        logger.error("SY fail query_product_price: error json")
        return {}


def query_product_detail(skus: list[str] = None, spus: list[str] = None, start_time: datetime = None,
                         end_time: datetime = None, page_index: int = None, warehouse_code: str = None) -> dict:
    message_data = {}
    if skus:
        message_data["Skus"] = skus
    if spus:
        message_data["Spus"] = spus
    if start_time:
        message_data["StartTime"] = start_time.strftime("%Y-%m-%d")
    if end_time:
        message_data["EndTime"] = end_time.strftime("%Y-%m-%d")
    if page_index is not None:
        message_data["PageIndex"] = page_index
    if warehouse_code:
        message_data["WarehouseCode"] = warehouse_code

    # 发送请求
    url = f"{API_BASE_URL}/api/Product/QueryProductDetail"
    return make_saleyee_request(url=url, message_data=message_data, token=APP_TOKEN, des_key=APP_KEY)


def parse_product(product_list):
    all_products = []
    create_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    for prod_info in product_list:
        sku = prod_info.get("Sku", "")
        published = 1 if prod_info.get("Published", False) else 0
        if published == 0 or len(sku) < 1:
            continue
        if db_get_one("basic_sy", f"sku='{sku}'", None):
            continue
        spu = prod_info.get("Spu", "")
        title = prod_info.get("EnName", "")
        url = ""
        if prod_info.get("Url", ""):
            url = "https://www.saleyee.cn/" + prod_info.get("Url", "")
        class_name = prod_info.get("CategoryFirstNameEN", "")
        product_type = prod_info.get("CategorySecondNameEN", "")
        package_length_cm = prod_info.get("SpecLength") if prod_info.get("SpecLength", None) else 1
        package_width_cm = prod_info.get("SpecWidth") if prod_info.get("SpecWidth", None) else 1
        package_height_cm = prod_info.get("SpecHeight") if prod_info.get("SpecHeight", None) else 1
        package_weight_g = prod_info.get("SpecWeight") if prod_info.get("SpecWeight", None) else 1
        brand = prod_info.get("BrandName", "")
        image_list = []

        if prod_info.get("GoodsImageList", []):
            image_list = [x.get("ImageUrl") for x in prod_info.get("GoodsImageList", [])]
            image_list = [x for x in image_list if x and len(x) > 5]
        extra_info = {"is_clear": prod_info.get("IsClear", False),
                      "ProductAttributeList": prod_info.get("ProductAttributeList", []),
                      "GoodsAttachmentList": prod_info.get("GoodsAttachmentList", []),
                      "TortDetail": prod_info.get("TortDetail", {}),
                      "DistributionLimit": prod_info.get("DistributionLimit", ""),
                      "GoodsDistributionPlatformDetailList": prod_info.get("GoodsDistributionPlatformDetailList", {}),
                      "IsProductAuth": prod_info.get("IsProductAuth", {})}

        not_ava_list = [x.get("DistributionPlatformDisplay") for x in
                        prod_info.get("GoodsDistributionPlatformDetailList", [])]
        not_available = ",".join(list(set(not_ava_list)))
        json_text = {"images_list": image_list,
                     "packages": [package_length_cm, package_width_cm, package_height_cm, package_weight_g]}
        product_info = ""
        desc_list = prod_info.get("GoodsDescriptionList", [])
        if desc_list:
            for desc in desc_list:
                product_info += desc.get("Title", "")
                product_info += "\nDescription:\n"
                paragraph_list = desc.get("GoodsDescriptionParagraphList", [])
                if paragraph_list:
                    for para in paragraph_list:
                        html_para = para.get("GoodsDescription", "")
                        product_info += f'\n{filter_sy_html_data(html_para)}\n'
        send_addr_list = prod_info.get("ProductSendAddressList", [])
        region = ""
        if send_addr_list:
            addr_list = [addr.get("WarehouseName") for addr in send_addr_list]
            region = ",".join(list(set(addr_list)))
        item = [sku, not_available, published, spu, title, url, class_name, product_type, brand, region, product_info,
                json.dumps(json_text), json.dumps(extra_info), create_time]
        all_products.append(item)
    db_batch_insert("basic_sy",
                    ["sku", "not_available", "published", "spu", "title", "link", "class_name", "product_type", "brand",
                     "region", "product_info", "json_text", "extra_info", "create_time"], all_products)


# 拉取上个月到今天的产品
def pull_all_product():
    for page in range(1, 9999):
        now = datetime.now()
        if now.month == 1:
            last_month_first_day = datetime(now.year - 1, 12, 1, tzinfo=timezone.utc)
        else:
            last_month_first_day = datetime(now.year, now.month - 1, 1, tzinfo=timezone.utc)
        last_day_of_current_month = calendar.monthrange(now.year, now.month)[1]
        this_month_last_day = datetime(now.year, now.month, last_day_of_current_month, 23, 59, 59, tzinfo=timezone.utc)
        start_time = last_month_first_day
        end_time = this_month_last_day
        res = query_product_detail(start_time=start_time, end_time=end_time, page_index=page)
        data = json.loads(res.get("Message", "{}"))
        if not data.get("ProductInfoList", []):
            break
        parse_product(data.get("ProductInfoList", []))


def sy_flash_not_available():
    sy_prod_list = db_list_by_page("basic_product", "id,sku", "platform='SY' and status =1 and published = 1", BasicProduct, 1, 999999)
    sku_id_map = {x.sku: x.id for x in sy_prod_list}
    sku_list = [x.sku for x in sy_prod_list]
    logger.info("SY start flush not_available......")
    for page in range(0, len(sku_list), 30):
        sleep_random_duration(1,5)
        this_page_sku = sku_list[page : page + 30]
        res = query_product_detail(skus=this_page_sku)
        data = json.loads(res.get("Message", "{}"))
        if not data.get("ProductInfoList", []):
            continue
        need_update = []
        for sy_prod in data.get("ProductInfoList", []):
            sku = sy_prod.get("Sku", "")
            not_ava_list = [x.get("DistributionPlatformDisplay") for x in
                            sy_prod.get("GoodsDistributionPlatformDetailList", [])]
            not_available = ",".join(list(set(not_ava_list))).lower()
            if not_available:
                if sku_id_map.get(sku, None):
                    need_update.append([sku_id_map.get(sku), not_available])
        if need_update:
            db_batch_update("basic_product",["id","not_available"], need_update)
    logger.info("SY end flush not_available......")


# 同步库存或者价格
def sy_update_price_quantity(is_price_update):
    sku_list_page = []
    for page in range(1, 99999):
        sku_list_this = db_list_by_page("basic_product", "id,published,sku,price,quantity", "platform = 'SY'", BasicProduct, page, 10000, "id", "desc")
        if not sku_list_this:
            break
        sku_list_page.extend(sku_list_this)
    logger.info(f'SY start: update {" price " if is_price_update else " quantity "}...')
    sleep_time = 0.6
    if is_price_update:
        sleep_time = 0.3
    for i in range(0, len(sku_list_page), 30):
        sku_list = sku_list_page[i:i + 30]
        if is_price_update:
            single_sy_price(sku_list)
        else:
            single_sy_quantity(sku_list)
        sleep_random_duration(sleep_time, sleep_time + 0.1)
        if i % 1800 == 0:
            sleep_random_duration(1, 2)
    logger.info(f"SY update {" price " if is_price_update else " quantity "}fetch completed.")


# 单次更加库存
def single_sy_quantity(sku_list):
    sku_id_map = {x.sku: x.id for x in sku_list}
    sku_quantity_map = {x.sku: x.quantity for x in sku_list}
    for_skus = [x.sku for x in sku_list]
    quantity_dict = query_product_inventory(for_skus)
    need_update_list = []
    if quantity_dict:
        for sku in for_skus:
            quantity = quantity_dict.get(sku) if quantity_dict.get(sku) else 0
            if quantity != sku_quantity_map.get(sku, 0):
                need_update_list.append([sku_id_map[sku], quantity])
        if need_update_list:
            db_batch_update("basic_product", ["id", "quantity"], need_update_list)


# 单次更新价格
def single_sy_price(sku_list):
    sku_id_map = {x.sku: x.id for x in sku_list}
    sku_published_map = {x.sku: x.published for x in sku_list}
    sku_price_map = {x.sku: float(x.price) for x in sku_list if x.price}
    for_skus = [x.sku for x in sku_list]
    price_dict = query_product_price(for_skus)
    need_update_list = []
    need_update_images_list = []
    if price_dict:
        for sku in for_skus:
            price = price_dict.get(sku) if price_dict.get(sku) else 0
            published = 0
            if price:
                published = 1
            if price != sku_price_map.get(sku, 0) or published != sku_published_map.get(sku, 1):
                need_update_list.append([sku_id_map[sku], published, price])
            if published != sku_published_map.get(sku, 1):
                need_update_images_list.append([sku_id_map[sku], published])
        if need_update_list:
            db_batch_update("basic_product", ["id", "published", "price"], need_update_list)
        if need_update_images_list:
            db_batch_update("basic_image", ["basic_id", "published"], need_update_images_list)


# 赛盈平台的商品 追加到 基础库
def sy_copy_data_to_basic():
    res_list = []
    for page in range(1, 9999):
        res_list_this = db_list_by_page("basic_sy", ",".join(BasicSy.fields), 'published = 1 and region like "%US%"  and sku not in (select sku from basic_product where platform = "SY")', BasicSy, page, 5000, "id", "desc")
        if not res_list_this:
            break
        res_list.extend(res_list_this)
    with ThreadPoolExecutor(max_workers=10) as executor:
        futures = []
        for item in res_list:
            futures.append(executor.submit(thread_copy_sy_to_basic, item))
            sleep_random_duration(0.1, 0.2)
        logger.info(f"SY to basic wait thread...")
        for future in as_completed(futures):
            try:
                future.result()
            except Exception as e:
                logger.error(f"SY to basic fail : {e}")


def thread_copy_sy_to_basic(prod: BasicSy):
    create_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    if prod.json_text and prod.product_info:
        db_prod = db_get_one("basic_product", f'platform="SY" and sku ="{prod.sku}"', BasicProduct)
        # if db_prod:
        #     return
        size_dict = ai_gen_size(prod.product_info)
        json_text_obj = json.loads(prod.json_text)
        # cm g 换算单位
        packages = convert_dimensions(json_text_obj.get("packages", []))
        packages_str = " * ".join([str(x) for x in packages])
        dimensions = [size_dict.get("item_length", 1), size_dict.get("item_width", 1), size_dict.get("item_height", 1),
                      packages[3]]
        dimensions_str = f'{dimensions[0]} * {dimensions[1]} * {dimensions[2]} * {dimensions[3]}'
        image_list = json_text_obj.get("images_list", [])
        image_list = [x for x in image_list if x and len(x) > 5]
        main_img = ""
        if image_list:
            main_img = image_list[0]
        json_text_obj["packages"] = packages
        json_text_obj["dimensions"] = dimensions
        json_text_obj["title"] = prod.title
        json_text_obj["sku"] = prod.sku
        json_text_obj["images_list"] = image_list
        insert_item = [db_prod.id, 0, prod.published, prod.not_available, "SY", prod.sku, prod.spu, main_img, prod.title,
                       dimensions_str, packages_str, json.dumps(json_text_obj), prod.product_info, create_time,
                       prod.link, prod.region, "000saleyee000", "saleyee"]
        db_fields = ["id", "status", "published", "not_available", "platform", "sku", "spu", "main_image", "title",
                     "dimensions", "packages", "json_text", "product_info", "create_time", "link", "region",
                     "supplier_code", "supplier_name"]
        db_batch_update("basic_product", db_fields, [insert_item])


def convert_dimensions(dimensions):
    if not dimensions:
        return [1, 1, 1, 1]
    cm_to_in = 0.393701
    g_to_lb = 0.00220462
    converted_dimensions = [
        round(dimensions[0] * cm_to_in, 2),
        round(dimensions[1] * cm_to_in, 2),
        round(dimensions[2] * cm_to_in, 2),
        round(dimensions[3] * g_to_lb, 2)
    ]
    return converted_dimensions


# 使用示例
if __name__ == "__main__":
    print(query_product_price(["06908547"]))
    pass
